#include "LoggerFile.h"
#include <chrono>
#include <cstdint>
#include <cstring>
#include <ctime>
#include <iostream>
#include <ostream>
#include <sched.h>
#include <string>
#include <sys/stat.h>
#include <sys/time.h>
#include "Common.h"

namespace xncc {

static constexpr size_t DATA_TYEPE_LEN = 1;

LogHeaderV1::LogHeaderV1(LOG_LEVEL      level,
                         thread_id_type pid,
                         const char*    file,
                         uint32_t       line,
                         const char*    func,
                         uint16_t       loggerId)
    : level_(level), pid_(pid), file_(file), line_(line), func_(func), loggerId_(loggerId)
{
    gettimeofday(&tv_, nullptr);
}

void LogHeaderV1::decode(std::string& str) const
{
    str += logLevelToStr(level_);
    str += common::timevalToStr(&tv_);
    str += "[";
    str += std::to_string(pid_);
    str += "]";
    str += BASEAME(file_);
    str += ":";
    str += std::to_string(line_);
    str += "(";
    str += func_;
    str += "):";
}

LogHeaderV2::LogHeaderV2(LOG_LEVEL      level,
                         thread_id_type pid,
                         const char*    file,
                         uint32_t       line,
                         const char*    func,
                         uint16_t       loggerId,
                         int32_t        fmtId)
    : level_(level), pid_(pid), file_(file), line_(line), func_(func), loggerId_(loggerId), fmtId_(fmtId)
{
    gettimeofday(&tv_, nullptr);
}

void LogHeaderV2::decode(std::string& str) const
{
    str += logLevelToStr(level_);
    str += common::timevalToStr(&tv_);
    str += "[";
    str += std::to_string(pid_);
    str += "]";
    str += BASEAME(file_);
    str += ":";
    str += std::to_string(line_);
    str += "(";
    str += func_;
    str += "):";
}

LogHeaderV3::LogHeaderV3(uint16_t loggerId, int32_t fmtId) : loggerId_(loggerId), fmtId_(fmtId)
{
}

LogHeaderV4::LogHeaderV4(uint16_t loggerId) : loggerId_(loggerId)
{
}

LogHeaderV5::LogHeaderV5(uint16_t loggerId) : loggerId_(loggerId)
{
}

LogHeaderV6::LogHeaderV6(uint16_t loggerId, int32_t fmtId) : loggerId_(loggerId), fmtId_(fmtId)
{
}

LoggerFile::LoggerFile(const LoggerConfig& config)
    : logDataLen_(0), fp_(nullptr), config_(config), rotateSize_(config.rotateSize * 1024 * 1024)
{
    if (!config.stdOut) {
        common::createPath(config.logPath);
        logFileName_ = getLogFileName();
        fp_          = fopen(logFileName_.c_str(), "w");
        if (fp_ == nullptr) {
            throw common::system_exception("log file " + logFileName_ + " create failed");
        }
    }
}
LoggerFile::~LoggerFile()
{
    if (fp_ != nullptr) {
        auto ret = fclose(fp_);
        if (ret != 0) {
            TRACE_SYSTEM("file:%s close failed", logFileName_.c_str());
        }
    }
}
auto LoggerFile::getLogFileName() -> std::string
{
    if (config_.nameFixed) {
        return config_.logPath + "/" + config_.logName;
    }
    std::string logFileName = common::appendPath(config_.logPath, config_.logName);
    logFileName += "_";
    logFileName += common::unixtimestampToStr(
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch())
            .count());
    logFileName += ".log";
    return logFileName;
}

void LoggerFile::flush()
{
    if (fp_ != nullptr) {
        auto ret = fflush(fp_);
        if (ret != 0) {
            TRACE_SYSTEM("file:%s flush failed", logFileName_.c_str());
        }
    }
}

void LoggerFile::writeLogMsg(const std::string& logdata)
{
    if (logdata.empty()) {
        return;
    }
    if (config_.stdOut) {
        std::cout << logdata;
        std::flush(std::cout);
        return;
    }
    if (!config_.nameFixed) {
        if (logDataLen_ > rotateSize_ && rotateSize_ > 0) {
            fclose(fp_);
            logFileName_ = getLogFileName();
            fp_          = fopen(logFileName_.c_str(), "w");
            if (!fp_) {
                throw common::system_exception("log file" + logFileName_ + " create failed");
            }
            logDataLen_ = 0;
        }
    }
    const auto ret = fwrite(logdata.c_str(), logdata.size(), 1, fp_);
    if (ret == 1) {
        logDataLen_ += logdata.size();
    }
    else {
        TRACE_SYSTEM("file:%s write failed", logFileName_.c_str());
    }
}

void LoggerFile::writeBinary(const char* buffer, std::size_t len)
{
    if (len <= 0) {
        return;
    }
    if (config_.stdOut) {
        std::cout << std::string(buffer, len);
        std::flush(std::cout);
        return;
    }
    if (!config_.nameFixed) {
        if (logDataLen_ > rotateSize_ && rotateSize_ > 0) {
            fclose(fp_);
            logFileName_ = getLogFileName();
            fp_          = fopen(logFileName_.c_str(), "w");
            if (fp_ == nullptr) {
                throw common::system_exception("log file" + logFileName_ + " create failed");
            }
            logDataLen_ = 0;
        }
    }

    const auto ret = fwrite(buffer, len, 1, fp_);
    if (ret == 1) {
        logDataLen_ += len;
    }
    else {
        TRACE_SYSTEM("file:%s write failed", logFileName_.c_str());
    }
}

}  // namespace xncc